package com.zhouyx.redisactive.redisson;

import java.util.concurrent.TimeUnit;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

/**
 * @ClassName SingleDistributedLockTemplate
 * @Description TODO
 * @Author za-zhouyunxing
 * @Date 2019/4/16 16:58
 * @Version 1.0
 */
public class SingleDistributedLockTemplate implements DistributedLockTemplate {
    private RedissonClient redisson;

    public SingleDistributedLockTemplate() {
    }

    public SingleDistributedLockTemplate(RedissonClient redisson) {
        this.redisson = redisson;
    }

    @Override
    public <T> T lock(DistributedLockCallback<T> callback, boolean fairLock) {
        return lock(callback, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT, fairLock);
    }

    @Override
    public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit, boolean fairLock) {
        RLock lock = getLock(callback.getLockName(), fairLock);
        try {
            lock.lock(leaseTime, timeUnit);
            return callback.process();
        } finally {
            if (lock != null && lock.isLocked()) {
                lock.unlock();
            }
        }
    }

    @Override
    public <T> T tryLock(DistributedLockCallback<T> callback, boolean fairLock) {
        return tryLock(callback, DEFAULT_WAIT_TIME, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT, fairLock);
    }

    @Override
    public <T> T tryLock(DistributedLockCallback<T> callback,
                         long waitTime,
                         long leaseTime,
                         TimeUnit timeUnit,
                         boolean fairLock) {
        RLock lock = getLock(callback.getLockName(), fairLock);
        try {
            if (lock.tryLock(waitTime, leaseTime, timeUnit)) {
                return callback.process();
            }
        } catch (InterruptedException e) {

        } finally {
            /*
             * 加锁后的业务逻辑处理完毕后，会进行“释放锁”操作，在释放的时候，
             * 有可能因为业务逻辑的处理时间较长，超过预设的leaseTime，默认5s，
             * 锁会被自动释放，这时若执行释放锁的逻辑，会抛一个类似“xx锁不被该线程所持有”的异常。
             * 所以上面代码，在执行释放锁之前的判断语句，
             * 应该换成if ( lock != null && lock.isHeldByCurrentThread() )。
             * 因为isLocked()方法是判断该锁是否被某一个线程所持有，而不能知道是哪一个线程持有。
             * 而isHeldByCurrentThread()的作用是检测该锁是否被当前线程所持有，当返回true的时候，
             * 就可以放心的去释放锁了（一般不会出问题了，除非在释放锁的前一刻，刚好时间到了）。
             * 不过，一般用到锁的逻辑，要尽可能的少，而不能把一大段代码塞到需要同步执行的逻辑中。
             */

//            if (lock != null && lock.isLocked()) {
//                lock.unlock();
//            }

            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return null;
    }

    private RLock getLock(String lockName, boolean fairLock) {
        RLock lock;
        if (fairLock) {
            lock = redisson.getFairLock(lockName);
        } else {
            lock = redisson.getLock(lockName);
        }
        return lock;
    }

    public void setRedisson(RedissonClient redisson) {
        this.redisson = redisson;
    }
}
